Fix bad git merge in main.cc from PR 126
authorRobert Lipe <robertlipe@gpsbabel.org>
Wed, 6 Dec 2017 21:52:36 +0000 (15:52 -0600)
committerRobert Lipe <robertlipe@gpsbabel.org>
Wed, 6 Dec 2017 21:52:36 +0000 (15:52 -0600)
gdb.cc
main.cc

diff --git a/gdb.cc b/gdb.cc
index 3ee2f5392e8c941c3aecca53800b93378431e050..e322d77566d7be347b09f9e34412c4b79f1b55ab 100644 (file)
--- a/gdb.cc
+++ b/gdb.cc
@@ -759,7 +759,7 @@ read_route()
     }
   }
 
-  // links = 0;
+  links = 0;
   points = FREAD_i32;
 
 #if GDB_DEBUG
diff --git a/main.cc b/main.cc
index ae8cc3476f8f27a122a6c83e992ac9ed6057d3f2..a7901bc338624ab3cc9b4ffbcb80af1bcfd3dce2 100644 (file)
--- a/main.cc
+++ b/main.cc
 #include "filterdefs.h"
 #include "inifile.h"
 #include "session.h"
+#include "src/core/file.h"
 #include "src/core/usasciicodec.h"
 #include <cctype>
 #include <clocale>
 #include <cstdio>
 #include <cstdlib>
-#include <csignal>
+#include <signal.h>
+#ifdef AFL_INPUT_FUZZING
+#include "argv-fuzz-inl.h"
+#endif
 
 #define MYNAME "main"
 
 void signal_handler(int sig);
 
-typedef struct arg_stack_s {
-  int argn;
-  int argc;
-  char** argv;
-  struct arg_stack_s* prev;
-} arg_stack_t;
-
-static arg_stack_t
-* push_args(arg_stack_t* stack, const int argn, const int argc, char* argv[])
+class QargStackElement
 {
-  arg_stack_t* res = (arg_stack_t*) xmalloc(sizeof(*res));
-
-  res->prev = stack;
-  res->argn = argn;
-  res->argc = argc;
-  res->argv = (char**)argv;
-
-  return res;
-}
-
-static arg_stack_t
-* pop_args(arg_stack_t* stack, int* argn, int* argc, char** argv[])
-{
-  arg_stack_t* res;
-  char** argv2 = *argv;
-  int i;
+public:
+  int argn;
+  QStringList qargs;
 
-  if (stack == NULL) {
-    fatal("main: Invalid point in time to call 'pop_args'\n");
+public:
+  QargStackElement()
+  {
   }
 
-  for (i = 0; i < *argc; i++) {
-    xfree(argv2[i]);
+  QargStackElement(int p_argn, QStringList p_qargs)
+  {
+    argn = p_argn;
+    qargs = p_qargs;
   }
-  xfree(*argv);
-
-  *argn = stack->argn;
-  *argc = stack->argc;
-  *argv = stack->argv;
+};
 
-  res = stack->prev;
-  xfree(stack);
-
-  return res;
-}
-
-static void
-load_args(const char* filename, int* argc, char** argv[])
+static QStringList
+load_args(const QString& filename, const QString& arg0)
 {
-  gbfile* fin;
-  char* str, *line = NULL;
-  int argc2;
-  char** argv2;
-
-  fin = gbfopen(filename, "r", "main");
-  while ((str = gbfgetstr(fin))) {
-    str = lrtrim(str);
-    if ((*str == '\0') || (*str == '#')) {
+  QString line;
+  QString str;
+  QStringList qargs(arg0);
+
+// Use a QTextStream to read the batch file.
+// By default that will use codecForLocale().
+// By default Automatic Unicode detection is enabled.
+  gpsbabel::File file(filename);
+  file.open(QFile::ReadOnly);
+  QTextStream stream(&file);
+  while (!(str = stream.readLine()).isNull()) {
+    str = str.trimmed();
+    if ((str.isEmpty()) || (str.at(0).toLatin1() == '#')) {
       continue;
     }
-
-    if (line == NULL) {
-      line = xstrdup(str);
+    if (line.isEmpty()) {
+      line = str;
     } else {
-      char* tmp;
-      xasprintf(&tmp, "%s %s", line, str);
-      xfree(line);
-      line = tmp;
+      line.append(' ');
+      line.append(str);
     }
   }
-  gbfclose(fin);
-
-  argv2 = (char**) xmalloc(2 * sizeof(*argv2));
-  argv2[0] = xstrdup(*argv[0]);
-  argc2 = 1;
-
-  str = csv_lineparse(line, " ", "\"", 0);
-  while (str != NULL) {
-    argv2 = (char**) xrealloc(argv2, (argc2 + 2) * sizeof(*argv2));
-    argv2[argc2] = xstrdup(str);
-    argc2++;
-    str = csv_lineparse(NULL, " ", "\"", 0);
+  file.close();
+
+  // We use csv_lineparse to protect quoted strings, otherwise
+  // we could just split on blank and eliminate the round trip
+  // to 8 bit characters and back.
+  // TODO: move csv processing to Qt, eliminating the need to go
+  // back to 8 bit encoding, which is shaky for encoding like utf8
+  // that have multibyte characters.
+  char* cbuff = xstrdup(CSTR(line));
+
+  char* cstr = csv_lineparse(cbuff, " ", "\"", 0);
+  while (cstr != NULL) {
+    qargs.append(QString::fromUtf8(cstr));
+    cstr = csv_lineparse(NULL, " ", "\"", 0);
   }
-  xfree(line);
-
-  argv2[argc2] = NULL;
 
-  *argc = argc2;
-  *argv = argv2;
+  xfree(cbuff);
+  return (qargs);
 }
 
 static void
@@ -221,13 +197,16 @@ print_extended_info()
 int
 main(int argc, char* argv[])
 {
+#ifdef AFL_INPUT_FUZZING
+  AFL_INIT_ARGV();
+#endif
   int c;
   int argn;
   ff_vecs_t* ivecs = NULL;
   ff_vecs_t* ovecs = NULL;
   filter_vecs_t* fvecs = NULL;
-  char* fname = NULL;
-  char* ofname = NULL;
+  QString fname;
+  QString ofname;
   const char* ivec_opts = NULL;
   const char* ovec_opts = NULL;
   const char* fvec_opts = NULL;
@@ -236,33 +215,22 @@ main(int argc, char* argv[])
   const char* prog_name = argv[0]; /* argv is modified during processing */
   queue* wpt_head_bak, *rte_head_bak, *trk_head_bak;   /* #ifdef UTF8_SUPPORT */
   signed int wpt_ct_bak, rte_ct_bak, trk_ct_bak;       /* #ifdef UTF8_SUPPORT */
-  arg_stack_t* arg_stack = NULL;
+  QStack<QargStackElement> qargs_stack = QStack<QargStackElement>();
 
-  // Create a QCoreApplication object to handle application initialization. 
-  // TODO: Someday we may actually use this, but for now we are just trying
-  // to get Qt initialized, especially locale related QTextCodec stuff.
+  // Create a QCoreApplication object to handle application initialization.
+  // In addition to being useful for argument decoding, the creation of a
+  // QCoreApplication object gets Qt initialized, especially locale related
+  // QTextCodec stuff.
   // For example, this will get the QTextCodec::codecForLocale set
   // correctly.
   QCoreApplication app(argc, argv);
-#ifdef __WIN32__
-  // Use QCoreApplication::arguments() to process the command line and replace
-  // argv[] strings with UTF-8 versions; this is needed on Windows only.
-  QVector<QByteArray> qargv;
-  {
-    QStringList qargs = QCoreApplication::arguments();
-    argc = qargs.size();
-    qargv.resize(argc);
-    for (int i = 0; i < argc; ++i) {
-      qargv[i] = qargs[i].toUtf8();
-      argv[i] = qargv[i].data();
-    }
-  }
-#endif
+  // Use QCoreApplication::arguments() to process the command line.
+  QStringList qargs = QCoreApplication::arguments();
 
   (void) new gpsbabel::UsAsciiCodec(); /* make sure a US-ASCII codec is available */
 
 #if (QT_VERSION < QT_VERSION_CHECK(5, 2, 0))
-  #error This version of Qt is not supported.
+#error This version of Qt is not supported.
 #endif
 
   // The first invocation of QTextCodec::codecForLocale() may result in LC_ALL being set to the native environment
@@ -312,8 +280,8 @@ main(int argc, char* argv[])
 #ifdef DEBUG_MEM
   debug_mem_open();
   debug_mem_output("command line: ");
-  for (argn = 1; argn < argc; argn++) {
-    debug_mem_output("%s ", argv[argn]);
+  for (int i = 1; i < qargs.size(); i++) {
+    debug_mem_output("%s ", qPrintable(qargs.at(i)));
   }
   debug_mem_output("\n");
 #endif
@@ -329,8 +297,8 @@ main(int argc, char* argv[])
   waypt_init();
   route_init();
 
-  if (argc < 2) {
-    usage(argv[0],1);
+  if (qargs.size() < 2) {
+    usage(prog_name,1);
     exit(0);
   }
 
@@ -338,68 +306,63 @@ main(int argc, char* argv[])
    * Open-code getopts since POSIX-impaired OSes don't have one.
    */
   argn = 1;
-  while (argn < argc) {
-    char* optarg;
+  while (argn < qargs.size()) {
+    QString optarg;
 
-    if (argv[argn][0] != '-') {
+//  we must check the length for afl input fuzzing to work.
+//    if (qargs.at(argn).at(0).toLatin1() != '-') {
+    if (qargs.at(argn).size() > 0 && qargs.at(argn).at(0).toLatin1() != '-') {
       break;
     }
-    if (argv[argn][1] == '-') {
+    if (qargs.at(argn).size() > 1 && qargs.at(argn).at(1).toLatin1() == '-') {
       break;
     }
 
-    if (argv[argn][1] == 'V') {
+    if (qargs.at(argn).size() > 1 && qargs.at(argn).at(1).toLatin1() == 'V') {
       printf("\nGPSBabel Version %s\n\n", gpsbabel_version);
-      if (argv[argn][2] == 'V') {
+      if (qargs.at(argn).size() > 2 && qargs.at(argn).at(2).toLatin1() == 'V') {
         print_extended_info();
       }
       exit(0);
     }
 
-    if (argv[argn][1] == '?' || argv[argn][1] == 'h') {
-      if (argn < argc-1) {
-        spec_usage(argv[argn+1]);
+    if (qargs.at(argn).size() > 1 && (qargs.at(argn).at(1).toLatin1() == '?' || qargs.at(argn).at(1).toLatin1() == 'h')) {
+      if (argn < qargs.size()-1) {
+        spec_usage(qPrintable(qargs.at(argn+1)));
       } else {
-        usage(argv[0],0);
+        usage(prog_name,0);
       }
       exit(0);
     }
 
-    c = argv[argn][1];
+    c = qargs.at(argn).size() > 1 ? qargs.at(argn).at(1).toLatin1() : '\0';
 
-    if (argv[argn][2]) {
-      opt_version = atoi(&argv[argn][2]);
+    if (qargs.at(argn).size() > 2) {
+      opt_version = qargs.at(argn).at(2).digitValue();
     }
 
     switch (c) {
-    //case 'c':
-    //  optarg = argv[argn][2] ? argv[argn]+2 : argv[++argn];
-    //  cet_convert_init(optarg, 1);
-    //  break;
     case 'i':
-      optarg = argv[argn][2]
-               ? argv[argn]+2 : argv[++argn];
-      ivecs = find_vec(optarg, &ivec_opts);
+      optarg = qargs.at(argn).size() > 2 ? QString(qargs.at(argn)).remove(0,2) : qargs.size()>(++argn) ? qargs.at(argn) : QString();
+      ivecs = find_vec(CSTR(optarg), &ivec_opts);
       if (ivecs == NULL) {
-        fatal("Input type '%s' not recognized\n", optarg);
+        fatal("Input type '%s' not recognized\n", qPrintable(optarg));
       }
       break;
     case 'o':
       if (ivecs == NULL) {
         warning("-o appeared before -i.   This is probably not what you want to do.\n");
       }
-      optarg = argv[argn][2]
-               ? argv[argn]+2 : argv[++argn];
-      ovecs = find_vec(optarg, &ovec_opts);
+      optarg = qargs.at(argn).size() > 2 ? QString(qargs.at(argn)).remove(0,2) : qargs.size()>(++argn) ? qargs.at(argn) : QString();
+      ovecs = find_vec(CSTR(optarg), &ovec_opts);
       if (ovecs == NULL) {
-        fatal("Output type '%s' not recognized\n", optarg);
+        fatal("Output type '%s' not recognized\n", qPrintable(optarg));
       }
       break;
     case 'f':
-      optarg = argv[argn][2]
-               ? argv[argn]+2 : argv[++argn];
+      optarg = qargs.at(argn).size() > 2 ? QString(qargs.at(argn)).remove(0,2) : qargs.size()>(++argn) ? qargs.at(argn) : QString();
       fname = optarg;
-      if (fname == NULL) {
+      if (fname.isEmpty()) {
         fatal("No file or device name specified.\n");
       }
       if (ivecs == NULL) {
@@ -419,8 +382,8 @@ main(int argc, char* argv[])
 
       cet_convert_init(ivecs->encode, ivecs->fixed_encode);    /* init by module vec */
 
-      start_session(ivecs->name, fname);
-      ivecs->rd_init(QString(fname));
+      start_session(ivecs->name, CSTR(fname));
+      ivecs->rd_init(fname);
       ivecs->read();
       ivecs->rd_deinit();
 
@@ -430,10 +393,9 @@ main(int argc, char* argv[])
       did_something = 1;
       break;
     case 'F':
-      optarg = argv[argn][2]
-               ? argv[argn]+2 : argv[++argn];
+      optarg = qargs.at(argn).size() > 2 ? QString(qargs.at(argn)).remove(0,2) : qargs.size()>(++argn) ? qargs.at(argn) : QString();
       ofname = optarg;
-      if (ofname == NULL) {
+      if (ofname.isEmpty()) {
         fatal("No output file or device name specified.\n");
       }
       if (ovecs && (!(global_opts.masked_objective & POSNDATAMASK))) {
@@ -452,7 +414,7 @@ main(int argc, char* argv[])
         trk_ct_bak = -1;
         rte_head_bak = trk_head_bak = NULL;
 
-        ovecs->wr_init(QString(ofname));
+        ovecs->wr_init(ofname);
 
         if (global_opts.charset != &cet_cs_vec_utf8) {
           /*
@@ -507,31 +469,9 @@ main(int argc, char* argv[])
     case 'T':
       global_opts.objective = posndata;
       global_opts.masked_objective |= POSNDATAMASK;
-      break;
-    case 'N':
-#if 0
-      /* This option is silently eaten for compatibilty.  -N is now the
-       * default.  If you want the old behaviour, -S allows you to individually
-       * turn them on.  The -N option will be removed in 2008.
-       */
-
-      switch (argv[argn][2]) {
-      case 'i':
-        global_opts.no_smart_icons = 1;
-        break;
-      case 'n':
-        global_opts.no_smart_names = 1;
-        break;
-      default:
-        global_opts.no_smart_names = 1;
-        global_opts.no_smart_icons = 1;
-        break;
-      }
-#endif
-
       break;
     case 'S':
-      switch (argv[argn][2]) {
+      switch (qargs.at(argn).size() > 2 ? qargs.at(argn).at(2).toLatin1() : '\0') {
       case 'i':
         global_opts.smart_icons = 1;
         break;
@@ -545,9 +485,8 @@ main(int argc, char* argv[])
       }
       break;
     case 'x':
-      optarg = argv[argn][2]
-               ? argv[argn]+2 : argv[++argn];
-      fvecs = find_filter_vec(optarg, &fvec_opts);
+      optarg = qargs.at(argn).size() > 2 ? QString(qargs.at(argn)).remove(0,2) : qargs.size()>(++argn) ? qargs.at(argn) : QString();
+      fvecs = find_filter_vec(CSTR(optarg), &fvec_opts);
 
       if (fvecs) {
         if (fvecs->f_init) {
@@ -559,13 +498,12 @@ main(int argc, char* argv[])
         }
         free_filter_vec(fvecs);
       }  else {
-        fatal("Unknown filter '%s'\n",optarg);
+        fatal("Unknown filter '%s'\n",qPrintable(optarg));
       }
       break;
     case 'D':
-      optarg = argv[argn][2]
-               ? argv[argn]+2 : argv[++argn];
-      global_opts.debug_level = atoi(optarg);
+      optarg = qargs.at(argn).size() > 2 ? QString(qargs.at(argn)).remove(0,2) : qargs.size()>(++argn) ? qargs.at(argn) : QString();
+      global_opts.debug_level = optarg.toInt();
       /*
        * When debugging, announce version.
        */
@@ -578,7 +516,7 @@ main(int argc, char* argv[])
      * Undocumented '-vs' option for GUI wrappers.
      */
     case 'v':
-      switch (argv[argn][2]) {
+      switch (qargs.at(argn).size() > 2 ? qargs.at(argn).at(2).toLatin1() : '\0') {
       case 's':
         global_opts.verbose_status = 1;
         break;
@@ -600,35 +538,39 @@ main(int argc, char* argv[])
       exit(0);
     case 'h':
     case '?':
-      usage(argv[0],0);
+      usage(prog_name,0);
       exit(0);
     case 'p':
-      optarg = argv[argn][2] ? argv[argn]+2 : argv[++argn];
+      optarg = qargs.at(argn).size() > 2 ? QString(qargs.at(argn)).remove(0,2) : qargs.size()>(++argn) ? qargs.at(argn) : QString();
       inifile_done(global_opts.inifile);
-      if (!optarg || strcmp(optarg, "") == 0) {        /* from GUI to preserve inconsistent options */
+      if (optarg.isEmpty()) {  /* from GUI to preserve inconsistent options */
         global_opts.inifile = NULL;
       } else {
         global_opts.inifile = inifile_init(optarg, MYNAME);
       }
       break;
     case 'b':
-      optarg = argv[argn][2] ? argv[argn]+2 : argv[++argn];
-      arg_stack = push_args(arg_stack, argn, argc, argv);
-      load_args(optarg, &argc, &argv);
-      if (argc == 0) {
-        arg_stack = pop_args(arg_stack, &argn, &argc, &argv);
+      optarg = qargs.at(argn).size() > 2 ? QString(qargs.at(argn)).remove(0,2) : qargs.size()>(++argn) ? qargs.at(argn) : QString();
+      qargs_stack.push(QargStackElement(argn, qargs));
+      qargs = load_args(optarg, qargs.at(0));
+      if (qargs.size() == 0) {
+        QargStackElement ele = qargs_stack.pop();
+        argn = ele.argn;
+        qargs = ele.qargs;
       } else {
         argn = 0;
       }
       break;
 
     default:
-      fatal("Unknown option '%s'.\n", argv[argn]);
+      fatal("Unknown option '%s'.\n", qPrintable(qargs.at(argn)));
       break;
     }
 
-    if ((argn+1 >= argc) && (arg_stack != NULL)) {
-      arg_stack = pop_args(arg_stack, &argn, &argc, &argv);
+    while ((argn+1 >= qargs.size()) && (!qargs_stack.isEmpty())) {
+      QargStackElement ele = qargs_stack.pop();
+      argn = ele.argn;
+      qargs = ele.qargs;
     }
     argn++;
   }
@@ -637,11 +579,12 @@ main(int argc, char* argv[])
    * Allow input and output files to be specified positionally
    * as well.  This is the typical command line format.
    */
-  argc -= argn;
-  argv += argn;
-  if (argc > 2) {
+  for (int i = 0; i < argn; i++) {
+    qargs.removeFirst();
+  }
+  if (qargs.size() > 2) {
     fatal("Extra arguments on command line\n");
-  } else if (argc && ivecs) {
+  } else if (qargs.size() && ivecs) {
     did_something = 1;
     /* simulates the default behaviour of waypoints */
     if (doing_nothing) {
@@ -650,18 +593,18 @@ main(int argc, char* argv[])
 
     cet_convert_init(ivecs->encode, 1);
 
-    start_session(ivecs->name, argv[0]);
+    start_session(ivecs->name, CSTR(qargs.at(0)));
     if (ivecs->rd_init == NULL) {
       fatal("Format does not support reading.\n");
     }
-    ivecs->rd_init(QString(argv[0]));
+    ivecs->rd_init(qargs.at(0));
     ivecs->read();
     ivecs->rd_deinit();
 
     cet_convert_strings(global_opts.charset, NULL, NULL);
     cet_convert_deinit();
 
-    if (argc == 2 && ovecs) {
+    if (qargs.size() == 2 && ovecs) {
       cet_convert_init(ovecs->encode, 1);
       cet_convert_strings(NULL, global_opts.charset, NULL);
 
@@ -669,13 +612,13 @@ main(int argc, char* argv[])
         fatal("Format does not support writing.\n");
       }
 
-      ovecs->wr_init(QString(argv[1]));
+      ovecs->wr_init(qargs.at(1));
       ovecs->write();
       ovecs->wr_deinit();
 
       cet_convert_deinit();
     }
-  } else if (argc) {
+  } else if (qargs.size()) {
     usage(prog_name,0);
     exit(0);
   }
@@ -711,11 +654,11 @@ main(int argc, char* argv[])
 
 
     if (ivecs->position_ops.rd_init) {
-      if (!fname) {
+      if (fname.isEmpty()) {
         fatal("An input file (-f) must be specified.\n");
       }
-      start_session(ivecs->name, fname);
-      ivecs->position_ops.rd_init(QString(fname));
+      start_session(ivecs->name, CSTR(fname));
+      ivecs->position_ops.rd_init(fname);
     }
 
     if (global_opts.masked_objective & ~POSNDATAMASK) {
@@ -733,7 +676,7 @@ main(int argc, char* argv[])
     }
 
     if (ovecs && ovecs->position_ops.wr_init) {
-      ovecs->position_ops.wr_init(QString(ofname));
+      ovecs->position_ops.wr_init(ofname);
     }
 
     tracking_status.request_terminate = 0;
@@ -750,7 +693,7 @@ main(int argc, char* argv[])
       }
       if (wpt) {
         if (ovecs) {
-//                                     ovecs->position_ops.wr_init(QString(ofname));
+//                                     ovecs->position_ops.wr_init(ofname);
           ovecs->position_ops.wr_position(wpt);
 //                                     ovecs->position_ops.wr_deinit();
         } else {